bitkeeper revision 1.1159.261.2 (420e3014BpIA6NnJTdNQGkfIDMtzmQ)
authormafetter@fleming.research <mafetter@fleming.research>
Sat, 12 Feb 2005 16:34:28 +0000 (16:34 +0000)
committermafetter@fleming.research <mafetter@fleming.research>
Sat, 12 Feb 2005 16:34:28 +0000 (16:34 +0000)
Added an in-memory ring buffer to which serial console output
can be temporarily redirected.  Mode is toggled by the "c" key on
the Xen console.
Nice hack for printk() intensive debugging modes.
When switching back to serial output, the current contents of the
buffer are first dumped.  Buffer defaults to 128Kb, but size can
be set on the Xen boot command line.

xen/common/keyhandler.c
xen/drivers/char/console.c
xen/include/asm-x86/config.h
xen/include/xen/console.h
xen/include/xen/serial.h

index 789b2d3c6aca4114d94f57299418e730007f6d66..fcbbce754bcffa1a57b67a84e61d15777a8845a7 100644 (file)
@@ -74,12 +74,14 @@ void register_irq_keyhandler(
 static void show_handlers(unsigned char key)
 {
     int i; 
+    int buffer_enable = sercon_buffer_bypass();
     printk("'%c' pressed -> showing installed handlers\n", key);
     for ( i = 0; i < KEY_MAX; i++ ) 
         if ( key_table[i].u.handler != NULL ) 
             printk(" key '%c' (ascii '%02x') => %s\n", 
                    (i<33 || i>126)?(' '):(i),i,
                    key_table[i].desc);
+    sercon_buffer_set(buffer_enable);
 }
 
 static void dump_registers(unsigned char key, struct xen_regs *regs)
@@ -168,6 +170,10 @@ void initialize_keytable(void)
 #ifndef NDEBUG
     register_keyhandler(
         'o', audit_domains_key,  "audit domains >0 EXPERIMENTAL"); 
+
+    register_keyhandler(
+        'c', sercon_buffer_toggle,
+        "toggle serial console output vs ring buffer capture");
 #endif
 
 #ifdef PERF_COUNTERS
index 009935efc0aed81c732de10dafa17f23f9eafd5a..1b55789cbbf1d7df988220ee47ef0d117a92cd0b 100644 (file)
@@ -50,6 +50,17 @@ static int vgacon_enabled = 0;
 
 spinlock_t console_lock = SPIN_LOCK_UNLOCKED;
 
+#ifndef NDEBUG
+static unsigned char *sercon_buffer      = NULL;
+static unsigned char *sercon_buffer_end  = NULL;
+static unsigned char *sercon_buffer_head = NULL;
+static unsigned char *sercon_buffer_next = NULL;
+
+static unsigned int opt_sercon_buffer_size = 128; /* kbytes */
+integer_param("conbuf", opt_sercon_buffer_size);
+
+static void sercon_buffer_puts(const unsigned char *s);
+#endif
 
 /*
  * *******************************************************
@@ -253,9 +264,13 @@ static void switch_serial_input(void)
     static char *input_str[2] = { "DOM0", "Xen" };
     xen_rx = !xen_rx;
     if ( SWITCH_CODE != 0 )
+    {
+        int buffer_enable = sercon_buffer_bypass();
         printk("*** Serial input -> %s "
                "(type 'CTRL-%c' three times to switch input to %s).\n",
                input_str[xen_rx], opt_conswitch[0], input_str[!xen_rx]);
+        sercon_buffer_set(buffer_enable);
+    }
 }
 
 static void __serial_rx(unsigned char c, struct xen_regs *regs)
@@ -352,7 +367,14 @@ long do_console_io(int cmd, int count, char *buffer)
 static inline void __putstr(const char *str)
 {
     int c;
-    serial_puts(sercon_handle, str);
+
+#ifndef NDEBUG
+    if ( sercon_handle & SERHND_BUFFERED )
+        sercon_buffer_puts(str);
+    else
+#endif
+        serial_puts(sercon_handle, str);
+
     while ( (c = *str++) != '\0' )
     {
         putchar_console(c);
@@ -462,22 +484,136 @@ void console_force_lock(void)
     spin_lock(&console_lock);
 }
 
+// 09Feb2005: this appears to be unused...
 void console_putc(char c)
 {
     serial_putc(sercon_handle, c);
 }
 
+// 09Feb2005: this appears to be unused...
 int console_getc(void)
 {
     return serial_getc(sercon_handle);
 }
 
+// 09Feb2005: this appears to be unused...
 int irq_console_getc(void)
 {
     return irq_serial_getc(sercon_handle);
 }
 
 
+/*
+ * **************************************************************
+ * *************** serial console ring buffer *******************
+ * **************************************************************
+ */
+
+#ifndef NDEBUG
+static void sercon_buffer_putc(const unsigned char c)
+{
+    if ( !sercon_buffer )
+        return;
+    if ( !c )
+        return;
+
+    if ( sercon_buffer_next == sercon_buffer_end )
+    {
+        // buffer wrap-around case...
+        sercon_buffer_head = sercon_buffer + 1;
+        sercon_buffer_next = sercon_buffer;
+    }
+    if ( sercon_buffer_head == sercon_buffer_next + 1 )
+    {
+        // the buffer is already full...
+        sercon_buffer_head++;
+    }
+    *sercon_buffer_next++ = c;
+    *sercon_buffer_next = 0;
+}
+
+static void sercon_buffer_puts(const unsigned char *s)
+{
+    // inefficient but simple...
+    while ( *s )
+        sercon_buffer_putc(*s++);
+}
+
+static void sercon_buffer_reset(void)
+{
+    sercon_buffer_head = sercon_buffer;
+    sercon_buffer_next = sercon_buffer;
+    sercon_buffer_head[0] = 0;
+}
+
+static void sercon_buffer_flush(void)
+{
+    serial_puts(sercon_handle, sercon_buffer_head);
+    if ( sercon_buffer_head != sercon_buffer )
+        serial_puts(sercon_handle, sercon_buffer);
+    sercon_buffer_reset();
+}
+
+void _sercon_buffer_dump(void)
+{
+    sercon_buffer_flush();
+    sercon_handle &= ~SERHND_BUFFERED;
+}
+
+void sercon_buffer_toggle(unsigned char key)
+{
+    if ( !sercon_buffer )
+    {
+        printk("serial console buffer not allocated\n");
+        return;
+    }
+
+    if ( sercon_handle & SERHND_BUFFERED )
+        sercon_buffer_flush();
+    sercon_handle ^= SERHND_BUFFERED;
+}
+
+void _sercon_buffer_set(int enable)
+{
+    if (enable)
+        sercon_handle |= SERHND_BUFFERED;
+    else
+        sercon_handle &= ~SERHND_BUFFERED;
+}
+
+int _sercon_buffer_bypass(void)
+{
+    int buffering = !!(sercon_handle & SERHND_BUFFERED);
+    sercon_handle &= ~SERHND_BUFFERED;
+
+    return buffering;
+}
+
+static int __init sercon_buffer_init(void)
+{
+    int order;
+    int kbytes = opt_sercon_buffer_size;
+
+    if ( !kbytes )
+        return 0;
+
+    order = get_order(kbytes * 1024);
+    sercon_buffer = (void *)alloc_xenheap_pages(order);
+    ASSERT( sercon_buffer );
+
+    sercon_buffer_end = sercon_buffer + kbytes*1024 - 1;
+    *sercon_buffer_end = 0;
+
+    sercon_buffer_reset();
+    sercon_handle |= SERHND_BUFFERED;
+
+    return 0;
+}
+__initcall(sercon_buffer_init);
+
+#endif /* not NDEBUG */
+
+
 /*
  * **************************************************************
  * *************** Debugging/tracing/error-report ***************
@@ -508,6 +644,8 @@ void panic(const char *fmt, ...)
     spin_unlock_irqrestore(&console_lock, flags);
 
     watchdog_on = 0;
+    sercon_buffer_dump();
+
     mdelay(5000);
     machine_restart(0);
 }
index 25d1a7657a97ae71c3ccd1f4f53430bb16aad784..cab524b2796252c13d94f4a96f9c59f32e84ef07 100644 (file)
@@ -99,10 +99,19 @@ extern void __out_of_line_bug(int line) __attribute__((noreturn));
 #define out_of_line_bug() __out_of_line_bug(__LINE__)
 #endif /* __ASSEMBLY__ */
 
+#ifndef __ASSEMBLY__
+#ifndef NDEBUG
+extern void _sercon_buffer_dump(void);
+#define sercon_buffer_dump() _sercon_buffer_dump()
+#else
+#define sercon_buffer_dump() ((void)0)
+#endif
 #define BUG() do {                                     \
        printk("BUG at %s:%d\n", __FILE__, __LINE__);   \
-       __asm__ __volatile__("ud2");                    \
+        watchdog_on = 0;                                \
+        sercon_buffer_dump();                           \
 } while (0)
+#endif /* __ASSEMBLY__ */
 
 #if defined(__x86_64__)
 
index abcb2fa1d8ef371b0ffec61dbf1e18e42f776262..7cc653595bd837f0e0a67e6ea84aa3a70139865b 100644 (file)
@@ -26,4 +26,22 @@ void console_putc(char c);
 int console_getc(void);
 int irq_console_getc(void);
 
+#ifdef NDEBUG
+#define sercon_buffer_bypass() (0)
+#else
+#define sercon_buffer_bypass() _sercon_buffer_bypass()
+int _sercon_buffer_bypass(void);
 #endif
+
+#ifdef NDEBUG
+#define sercon_buffer_set(_enable) ((void)(0 && (_enable)));
+#else
+#define sercon_buffer_set(_enable) _sercon_buffer_set(_enable)
+void _sercon_buffer_set(int enable);
+#endif
+
+#ifndef NDEBUG
+void sercon_buffer_toggle(unsigned char key);
+#endif
+
+#endif /* __CONSOLE_H__ */
index 5c40db3e7d3dcc5e238621f48a566713fd78e92b..2a337145643ad94bdc6378dda78a102fa613b315 100644 (file)
@@ -18,6 +18,7 @@
 #define SERHND_HI       (1<<1) /* Mux/demux each transferred char by MSB. */
 #define SERHND_LO       (1<<2) /* Ditto, except that the MSB is cleared.  */
 #define SERHND_COOKED   (1<<3) /* Newline/carriage-return translation?    */
+#define SERHND_BUFFERED (1<<4) /* Console serial port ring buffered?      */
 
 /* Two-stage initialisation (before/after IRQ-subsystem initialisation). */
 void serial_init_stage1(void);